package com.yeskery.nut.application.netty;

import com.yeskery.nut.application.NutApplication;
import com.yeskery.nut.application.NutServer;
import com.yeskery.nut.application.NutServerConfigure;
import com.yeskery.nut.core.*;
import com.yeskery.nut.util.ClassUtils;
import com.yeskery.nut.websocket.WebSocketConfiguration;
import com.yeskery.nut.websocket.WebSocketConfigurationRegistry;
import com.yeskery.nut.websocket.WebSocketServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.logging.Logger;

/**
 * 基于Netty的HttpServer实现HTTP服务
 * @author sprout
 * 2022-06-16 10:50
 */
public class NettyNutServer implements NutServer, WebSocketServer {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(NettyNutServer.class.getName());

    /** WebSocket依赖检查class名称 */
    private static final String WS_DEPEND_CHECK_CLASS_NAMES = "io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler";

    /** websocket上下文工厂 */
    private final WebSocketServerContextFactory webSocketServerContextFactory = new WebSocketServerContextFactory();

    /** WebSocket上下文 */
    private WebSocketServerContext webSocketServerContext = new EmptyWebSocketServerContext();

    /** 主循环事件监听组 */
    private EventLoopGroup bossGroup;

    /** 工作循环事件监听组 */
    private EventLoopGroup workGroup;

    /** 是否安全方式启动 */
    protected boolean secure = false;

    @Override
    public void startServer(NutServerConfigure nutServerConfigure) throws Exception {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bossGroup = new NioEventLoopGroup();
        workGroup = new NioEventLoopGroup();
        bootstrap.group(bossGroup, workGroup)
                .handler(new LoggingHandler(LogLevel.DEBUG))
                .channel(NioServerSocketChannel.class)
                .childHandler(buildHttpServerInitializer(nutServerConfigure.getNutApplication(), webSocketServerContext,
                        nutServerConfigure.getServerContext(), nutServerConfigure.getDispatcher(),
                        nutServerConfigure.getSessionManager(), nutServerConfigure.getServerRequestConfiguration()))
                .childOption(ChannelOption.SO_KEEPALIVE, true);

        ChannelFuture channelFuture = bootstrap.bind(new InetSocketAddress(nutServerConfigure.getPort())).sync();
        logger.info(getServerStartedTip(nutServerConfigure.getPort(), "Netty", secure));
        channelFuture.channel().closeFuture().sync();
    }

    @Override
    public void close() throws IOException {
        try {
            if (bossGroup != null) {
                bossGroup.shutdownGracefully().sync();
            }
            if (workGroup != null) {
                workGroup.shutdownGracefully().sync();
            }
        } catch (InterruptedException e) {
            throw new IOException("Netty Server Close Failure.", e);
        }
    }

    @Override
    public void checkDependExist() throws Exception {
        if (!ClassUtils.isExistTargetClass(WS_DEPEND_CHECK_CLASS_NAMES)) {
            throw new NutException("Netty Server Used WebSocket, Such As @WebSocket Annotation, But Not Found " +
                    "Netty WebSocket Library, Check Has Been Import netty-all?");
        }
    }

    @Override
    public void registerEndpoints(Collection<WebSocketConfiguration> webSocketConfigurations) {
        WebSocketConfigurationRegistry webSocketConfigurationRegistry = new WebSocketConfigurationRegistry();
        webSocketConfigurationRegistry.registerEndpoints(webSocketConfigurations);
        webSocketServerContext = webSocketServerContextFactory.buildWebSocketServerContext(webSocketConfigurationRegistry, secure);
    }

    /**
     * 构建HttpServer初始化对象
     * @param nutApplication Nut应用对象
     * @param webSocketServerContext WebSocket上下文
     * @param serverContext 服务上下文
     * @param dispatcher 请求分发器
     * @param sessionManager 会话管理器
     * @param serverRequestConfiguration 服务请求配置对象
     * @return HttpServer初始化对象
     */
    protected HttpServerInitializer buildHttpServerInitializer(NutApplication nutApplication, WebSocketServerContext webSocketServerContext,
                                                               ServerContext serverContext, Dispatcher dispatcher, SessionManager sessionManager,
                                                               ServerRequestConfiguration serverRequestConfiguration) {
        return new HttpServerInitializer(nutApplication, webSocketServerContext, serverContext, dispatcher, sessionManager,
                serverRequestConfiguration, this);
    }
}
